﻿using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using log4net;

namespace MPMovieCatalog.Util
{
    public class WebSession
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(WebSession));

        private string m_cookieHeader = string.Empty;

        public WebSession()
        {
        }

        public bool Retrieve(string requestUrl, Encoding encoding, out string data)
        {
            string responseUrl = string.Empty;
            return Retrieve(requestUrl, encoding, out responseUrl, out data);
        }

        public bool Retrieve(string requestUrl, out string data)
        {
            string responseUrl = string.Empty;
            return Retrieve(requestUrl, null, out responseUrl, out data);
        }

        public bool Retrieve(string requestUrl, out string responseUrl, out string data)
        {
            return Retrieve(requestUrl, null, out responseUrl, out data);
        }

        public bool Retrieve(string requestUrl, Encoding encoding, out string responseUrl, out string data)
        {
            responseUrl = string.Empty;
            data = string.Empty;

            int timeout = 5000;
            int timeoutIncrement = 2000;
            int maxRetries = 5;

            int tryCount = 0;

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUrl);

            while (data == string.Empty)
            {
                tryCount++;
                try
                {
                    request.UserAgent = "Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.0; en-US)";
                    request.Accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
                    request.Timeout = timeout + (timeoutIncrement * maxRetries);
                    request.CookieContainer = new CookieContainer();
                    if (m_cookieHeader != null)
                        request.CookieContainer.SetCookies(request.RequestUri, m_cookieHeader);
                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                    m_cookieHeader = request.CookieContainer.GetCookieHeader(request.RequestUri);
                    responseUrl = response.ResponseUri.AbsoluteUri;

                    // Get result as stream
                    Stream resultData = response.GetResponseStream();

                    // If encoding was not set manually try to detect it
                    if (encoding == null)
                    {
                        try
                        {
                            // Try to get the encoding using the characterset
                            encoding = Encoding.GetEncoding(response.CharacterSet);
                        }
                        catch (System.Exception e)
                        {
                            // If this fails default to the system's default encoding
                            encoding = Encoding.Default;
                        }
                    }

                    // Converts the stream to a string
                    StreamReader reader = new StreamReader(resultData, encoding, true);
                    data = reader.ReadToEnd();

                    // Close stream and response objects
                    resultData.Close();
                    reader.Close();
                    response.Close();
                }
                catch (WebException e)
                {

                    // Skip retry logic on protocol errors
                    if (e.Status == WebExceptionStatus.ProtocolError)
                    {
                        HttpStatusCode statusCode = ((HttpWebResponse)e.Response).StatusCode;
                        switch (statusCode)
                        {
                            // Currently the only exception is the service temporarily unavailable status
                            // So keep retrying when this is the case
                            case HttpStatusCode.ServiceUnavailable:
                                break;
                            // all other status codes mostly indicate problems that won't be
                            // solved within the retry period so fail these immediatly
                            default:
                                log.Error(string.Format("Connection failed: URL={0}, Status={1}, Description={2}.", requestUrl, statusCode, ((HttpWebResponse)e.Response).StatusDescription), e);
                                return false;
                        }
                    }

                    // Return when hitting maximum retries.
                    if (tryCount == maxRetries)
                    {
                        log.Error(string.Format("Connection failed: Reached retry limit of {0}. URL={1}", maxRetries, requestUrl), e);
                        return false;
                    }
                    else
                    {
                        log.Debug(string.Format("Connection retry ({0}): URL={1}, Status={2}. Exception: {3} ", tryCount.ToString(), requestUrl, e.Status.ToString(), e.Message), e);
                    }

                    // If we did not experience a timeout but some other error
                    // use the timeout value as a pause between retries
                    if (e.Status != WebExceptionStatus.Timeout)
                    {
                        Thread.Sleep(timeout + (timeoutIncrement * tryCount));
                    }
                }
            }
            return true;
        }
    }
}
